<template>
  <a-select
    class="w-full"
    showSearch
    allowClear
    labelInValue
    :mode="mode"
    :getPopupContainer="getPopupContainer"
    :disabled="disabled"
    :placeholder="placeholder"
    optionFilterProp="children"
    v-model="selectedValue"
    :notFoundContent="loading ? undefined : null"
    @change="handleChange"
    @search="handleSearch"
    @select="handleSelect"
    @popupScroll="handlePopupScroll"
    @dropdownVisibleChange="dropdownVisibleChange"
  >
    <a-spin v-if="loading" slot="notFoundContent" size="small"/>
    <a-select-option v-for="d in renderedOptions" :key="d.value" :value="d.key">{{ d.label }}</a-select-option>
  </a-select>

</template>

<script>
import debounce from 'lodash/debounce'
import { getAction } from '@/services/manage'
import { isEmpty } from '@/utils/util'
export default {
  name: 'ZSyncSelectTag',
  props: {
    disabled: Boolean,
    value: [String, Number],
    options: Array,
    placeholder: {
      type: String,
      default: '请选择',
      required: false
    },
    url: String,
    optionKey: {
      type: Object,
      default: () => ({ label: 'label', value: 'value' })
    },
    num: { // 加载条数--可自定义
      type: Number,
      default: 30
    },
    mode: String, // multiple || default
    getPopupContainer: {
      type: Function,
      default: () => document.body
    },
    popupContainer: String // 自定义属性，方便设置下拉框popupContainer属性 body|parent
  },
  data() {
    return {
      loading: false,
      selectedValue: '',
      oriDataList: [], // 原数据列表 -- 从接口获取
      dataLoading: false, // 原数据列表的加载状态 -- 接口的响应状态
      searchVal: '', // 搜索的内容
      filterDataList: [], // 过滤的数据列表 -- 从dataList中过滤出含搜索内容的数据
      renderedOptions: [], // 已渲染的下拉列表
      oriDataMap: new Map()
    }
  },
  watch: {
    'value': {
      handler(val) {
        this.setSelectValue(val)
      }
    },
    'url': {
      async handler(val) {
        await this.loadData()
        this.setSelectValue(this.value)
      },
      immediate: true
    },
    options: {
      handler(val) {
        if (val) {
          this.setOptions(val)
          this.setSelectValue(this.value)
        }
      },
      deep: true
    }
  },
  mounted() {
    this.selectedValue = this.mode === 'multiple' ? [] : {}
  },
  methods: {
    async loadData() {
      if (this.options) {
        this.setOptions(this.options)
      } else {
        if (!this.url) {
          this.oriDataList = []
          this.renderedOptions = []
          return
        }
        this.loading = true
        const res = await getAction(this.url)
        this.loading = false
        if (res.success) {
          this.setOptions(res.result)
        } else {
          this.$message.warning(res.message)
        }
      }
    },
    setOptions(list) {
      this.oriDataList = []
      this.oriDataMap.clear()
      list.forEach(item => {
        const data = {
          label: item[this.optionKey.label],
          key: item[this.optionKey.value]
        }
        this.oriDataList.push(data)
        this.oriDataMap.set(data.key, item)
      })
      this.renderedOptions = this.oriDataList.slice(0, this.num)
    },
    handleChange(selectedValue) {
      if (this.mode === 'multiple') {
        const value = []
        const selectOptions = []
        selectedValue.forEach(item => {
          value.push(item.key)
          selectOptions.push(this.oriDataMap.get(item.key) || { [this.optionKey.value]: item.key })
        })
        this.$emit('change', value.join(), selectOptions)
      } else {
        this.selectedValue = selectedValue
        const value = this.selectedValue?.key
        this.$emit('change', value, value ? this.oriDataMap.get(value) || { [this.optionKey.value]: value } : {})
      }
    },
    handleSearch(val) {
      this.searchVal = val
      let filterList = []
      if (val) {
        filterList = (this.oriDataList).filter(item => {
          return item.label && item.label.indexOf(val) > -1
        })
      } else {
        filterList = this.oriDataList
      }
      this.filterDataList = filterList
      this.renderedOptions = filterList.length < this.num ? filterList : filterList.slice(0, this.num)
    },
    handlePopupScroll: debounce(function(e) { // 滚动时触发（防止抖动）
      const { scrollHeight, scrollTop, clientHeight } = e.target
      if (scrollHeight - scrollTop === clientHeight) {
        if (this.searchVal === '') {
          this.loadMoreData(this.oriDataList)
        } else {
          this.loadMoreData(this.filterDataList)
        }
      }
    }, 400),
    // 加载更多数据到select框
    loadMoreData(dataList) {
      const renderedLen = this.renderedOptions.length// 已渲染的下拉列表长度
      const totalLen = dataList.length // 当前数据源的长度
      let addList = []
      if (renderedLen < totalLen) {
        if (renderedLen + this.num <= totalLen) {
          addList = dataList.slice(renderedLen, renderedLen + this.num)
        } else {
          addList = dataList.slice(renderedLen,
            renderedLen + (totalLen % this.num))
        }
        this.renderedOptions = (this.renderedOptions).concat(addList)
      }
    },
    // 被选中时调用，参数为选中项的value(或 key) 值
    handleSelect(select) {
      const val = select.key
      if (this.searchVal) {
        const selectedArr = (this.oriDataList).filter(item => item.value === val)// 从数据源中过滤出下拉框选中的值，并返回一个数组
        const restList = (this.oriDataList).filter(item => item.value !== val)// 从数据源中过滤出其他的值，返回一个数组
        const newList = selectedArr.concat(restList).slice(0, this.num)// 将选中的元素放到下拉列表的第一位
        this.renderedOptions = newList// 更新已渲染的下拉列表
        this.oriDataList = selectedArr.concat(restList)// 更新数据源
        this.searchVal = ''// 因为触发handleSelect函数时，会自动清空用户输入的内容。因此，searchVal需要重置。
      }
    },
    // 组装select选中值
    setSelectValue(val) {
      if (isEmpty(val)) {
        this.selectedValue = this.mode === 'multiple' ? [] : ''
        return
      }
      if (this.mode === 'multiple') { // 多选
        const list = val.split(',')
        const selectedObj = this.getSelectValue(val)
        const selectedValue = []
        list.forEach(item => { // 按顺序展示
          selectedValue.push(selectedObj[item])
        })
        this.selectedValue = selectedValue
      } else { // 单选
        const selectedObj = this.getSelectValue(val)
        this.selectedValue = selectedObj[val]
        // if (!this.selectedValue || this.selectedValue.key !== val || !isNil(this.selectedValue.label)) {
        //   const selectedObj = this.getSelectValue(val)
        //   this.selectedValue = selectedObj[val]
        // }
      }
    },
    getSelectValue(value) {
      const valueList = value.split(',')
      const options = {} // 缓存命中集合
      valueList.forEach(key => {
        const item = this.oriDataMap.get(key)
        options[key] = item ? { key: key, label: item[this.optionKey.label] } : { key: key, label: key }
      })
      return options
    },
    getPopupContainerFn(node) {
      if (this.popupContainer === 'body') {
        return document.body
      } else if (this.popupContainer === 'parent') {
        return node.parentNode
      }
      return this.getPopupContainer(node)
    },
    dropdownVisibleChange(open) { // 修复多选模式，未搜索到内容，无法再次展开下拉框
      if (this.mode === 'multiple' && this.searchVal && open) {
        this.handleSearch('')
      }
    }
  },
  model: {
    prop: 'value',
    event: 'change'
  }
}
</script>
